mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-24 04:17:08 +08:00
1007 lines
35 KiB
C#
1007 lines
35 KiB
C#
#region copyright
|
||
//------------------------------------------------------------------------------
|
||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||
// QQ群:605534569
|
||
//------------------------------------------------------------------------------
|
||
#endregion
|
||
|
||
//------------------------------------------------------------------------------
|
||
// 此代码版权(除特别声明或在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首页:http://rrqm_home.gitee.io/touchsocket/
|
||
// 交流QQ群:234762506
|
||
// 感谢您的下载和使用
|
||
//------------------------------------------------------------------------------
|
||
//------------------------------------------------------------------------------
|
||
using System.Net.Sockets;
|
||
using System.Runtime.InteropServices;
|
||
|
||
namespace ThingsGateway.Foundation.Sockets
|
||
{
|
||
/// <summary>
|
||
/// 简单Tcp客户端
|
||
/// </summary>
|
||
public class TcpClient : TcpClientBase
|
||
{
|
||
/// <summary>
|
||
/// 接收到数据
|
||
/// </summary>
|
||
public ReceivedEventHandler<TcpClient> Received { get; set; }
|
||
|
||
/// <inheritdoc/>
|
||
protected override async Task ReceivedData(ReceivedDataEventArgs e)
|
||
{
|
||
if (this.Received != null)
|
||
{
|
||
await this.Received.Invoke(this, e);
|
||
if (e.Handled)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
await base.ReceivedData(e);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Tcp客户端
|
||
/// </summary>
|
||
[System.Diagnostics.DebuggerDisplay("{IP}:{Port}")]
|
||
public class TcpClientBase : BaseSocket, ITcpClient
|
||
{
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
public TcpClientBase()
|
||
{
|
||
this.Protocol = Protocol.Tcp;
|
||
this.m_tcpCore = new InternalTcpCore();
|
||
}
|
||
|
||
#region 变量
|
||
|
||
private DelaySender m_delaySender;
|
||
private volatile bool m_online;
|
||
private readonly EasyLock m_semaphore = new();
|
||
private readonly InternalTcpCore m_tcpCore;
|
||
#endregion 变量
|
||
|
||
#region 事件
|
||
|
||
/// <inheritdoc/>
|
||
public ConnectedEventHandler<ITcpClient> Connected { get; set; }
|
||
|
||
/// <inheritdoc/>
|
||
public ConnectingEventHandler<ITcpClient> Connecting { get; set; }
|
||
|
||
/// <inheritdoc/>
|
||
public DisconnectEventHandler<ITcpClientBase> Disconnected { get; set; }
|
||
|
||
/// <inheritdoc/>
|
||
public DisconnectEventHandler<ITcpClientBase> Disconnecting { get; set; }
|
||
|
||
private Task PrivateOnConnected(object o)
|
||
{
|
||
return this.OnConnected((ConnectedEventArgs)o);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 已经建立Tcp连接
|
||
/// </summary>
|
||
/// <param name="e"></param>
|
||
protected virtual async Task OnConnected(ConnectedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (this.Connected != null)
|
||
{
|
||
await this.Connected.Invoke(this, e);
|
||
if (e.Handled)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
await this.PluginsManager.RaiseAsync(nameof(ITcpConnectedPlugin.OnTcpConnected), this, e);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Connected)}中发生错误。", ex);
|
||
}
|
||
}
|
||
|
||
private Task PrivateOnConnecting(ConnectingEventArgs e)
|
||
{
|
||
if (this.CanSetDataHandlingAdapter)
|
||
{
|
||
this.SetDataHandlingAdapter(this.Config.GetValue(TouchSocketConfigExtension.TcpDataHandlingAdapterProperty).Invoke());
|
||
}
|
||
|
||
return this.OnConnecting(e);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 准备连接的时候,此时已初始化Socket,但是并未建立Tcp连接
|
||
/// </summary>
|
||
/// <param name="e"></param>
|
||
protected virtual async Task OnConnecting(ConnectingEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (this.Connecting != null)
|
||
{
|
||
await this.Connecting.Invoke(this, e);
|
||
if (e.Handled)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
await this.PluginsManager.RaiseAsync(nameof(ITcpConnectingPlugin.OnTcpConnecting), this, e);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.OnConnecting)}中发生错误。", ex);
|
||
}
|
||
}
|
||
|
||
private Task PrivateOnDisconnected(object obj)
|
||
{
|
||
this.m_receiver?.TryInputReceive(default, default);
|
||
return this.OnDisconnected((DisconnectEventArgs)obj);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 断开连接。在客户端未设置连接状态时,不会触发
|
||
/// </summary>
|
||
/// <param name="e"></param>
|
||
protected virtual async Task OnDisconnected(DisconnectEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (this.Disconnected != null)
|
||
{
|
||
await this.Disconnected.Invoke(this, e).ConfigureAwait(false);
|
||
if (e.Handled)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
await this.PluginsManager.RaiseAsync(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e).ConfigureAwait(false);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Disconnected)}中发生错误。", ex);
|
||
}
|
||
}
|
||
|
||
private Task PrivateOnDisconnecting(object obj)
|
||
{
|
||
return this.OnDisconnecting((DisconnectEventArgs)obj);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 即将断开连接(仅主动断开时有效)。
|
||
/// </summary>
|
||
/// <param name="e"></param>
|
||
protected virtual async Task OnDisconnecting(DisconnectEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (this.Disconnecting != null)
|
||
{
|
||
await this.Disconnecting.Invoke(this, e).ConfigureAwait(false);
|
||
if (e.Handled)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
await this.PluginsManager.RaiseAsync(nameof(ITcpDisconnectingPlugin.OnTcpDisconnecting), this, e).ConfigureAwait(false);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Disconnecting)}中发生错误。", ex);
|
||
}
|
||
}
|
||
|
||
#endregion 事件
|
||
|
||
#region 属性
|
||
|
||
/// <inheritdoc/>
|
||
public DateTime LastReceivedTime => this.GetTcpCore().ReceiveCounter.LastIncrement;
|
||
|
||
/// <inheritdoc/>
|
||
public DateTime LastSendTime => this.GetTcpCore().SendCounter.LastIncrement;
|
||
|
||
/// <inheritdoc/>
|
||
public IContainer Container { get; private set; }
|
||
|
||
/// <inheritdoc/>
|
||
public virtual bool CanSetDataHandlingAdapter => true;
|
||
|
||
/// <inheritdoc/>
|
||
public TouchSocketConfig Config { get; private set; }
|
||
|
||
/// <inheritdoc/>
|
||
public SingleStreamDataHandlingAdapter DataHandlingAdapter { get; private set; }
|
||
|
||
/// <inheritdoc/>
|
||
public string IP { get; private set; }
|
||
|
||
/// <inheritdoc/>
|
||
public Socket MainSocket { get; private set; }
|
||
|
||
/// <inheritdoc/>
|
||
public bool Online { get => this.m_online; }
|
||
|
||
/// <inheritdoc/>
|
||
public bool CanSend => this.m_online;
|
||
|
||
/// <inheritdoc/>
|
||
public IPluginsManager PluginsManager { get; private set; }
|
||
|
||
/// <inheritdoc/>
|
||
public int Port { get; private set; }
|
||
|
||
|
||
|
||
/// <inheritdoc/>
|
||
public bool UseSsl => this.GetTcpCore().UseSsl;
|
||
|
||
/// <inheritdoc/>
|
||
public Protocol Protocol { get; set; }
|
||
|
||
/// <inheritdoc/>
|
||
public IPHost RemoteIPHost { get; private set; }
|
||
|
||
/// <inheritdoc/>
|
||
public bool IsClient => true;
|
||
|
||
#endregion 属性
|
||
|
||
#region 断开操作
|
||
|
||
/// <inheritdoc/>
|
||
public virtual void Close(string msg = TouchSocketCoreUtility.Empty)
|
||
{
|
||
lock (this.SyncRoot)
|
||
{
|
||
if (this.m_online)
|
||
{
|
||
Task.Factory.StartNew(this.PrivateOnDisconnecting, new DisconnectEventArgs(true, msg));
|
||
this.MainSocket.TryClose();
|
||
this.BreakOut(true, msg);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
/// <param name="disposing"></param>
|
||
protected override void Dispose(bool disposing)
|
||
{
|
||
lock (this.SyncRoot)
|
||
{
|
||
if (this.m_online)
|
||
{
|
||
Task.Factory.StartNew(this.PrivateOnDisconnecting, new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开"));
|
||
this.BreakOut(true, $"{nameof(Dispose)}主动断开");
|
||
}
|
||
}
|
||
base.Dispose(disposing);
|
||
}
|
||
|
||
#endregion 断开操作
|
||
|
||
#region Connect
|
||
|
||
/// <summary>
|
||
/// 建立Tcp的连接。
|
||
/// </summary>
|
||
/// <param name="timeout"></param>
|
||
/// <exception cref="ObjectDisposedException"></exception>
|
||
/// <exception cref="ArgumentNullException"></exception>
|
||
/// <exception cref="Exception"></exception>
|
||
/// <exception cref="TimeoutException"></exception>
|
||
protected void TcpConnect(int timeout)
|
||
{
|
||
lock (this.SyncRoot)
|
||
{
|
||
if (this.m_online)
|
||
{
|
||
return;
|
||
}
|
||
if (this.DisposedValue)
|
||
{
|
||
throw new ObjectDisposedException(this.GetType().FullName);
|
||
}
|
||
if (this.Config == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(this.Config), "配置文件不能为空。");
|
||
}
|
||
var iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException(nameof(IPHost), "iPHost不能为空。");
|
||
this.MainSocket.SafeDispose();
|
||
var socket = this.CreateSocket(iPHost);
|
||
this.PrivateOnConnecting(new ConnectingEventArgs(socket)).GetFalseAwaitResult();
|
||
if (timeout == 5000)
|
||
{
|
||
socket.Connect(iPHost.Host, iPHost.Port);
|
||
}
|
||
else
|
||
{
|
||
var task = Task.Run(() =>
|
||
{
|
||
socket.Connect(iPHost.Host, iPHost.Port);
|
||
});
|
||
task.ConfigureAwait(false);
|
||
if (!task.Wait(timeout))
|
||
{
|
||
socket.SafeDispose();
|
||
throw new TimeoutException();
|
||
}
|
||
}
|
||
this.m_online = true;
|
||
this.SetSocket(socket);
|
||
this.BeginReceive();
|
||
this.PrivateOnConnected(new ConnectedEventArgs())
|
||
.ConfigureAwait(false)
|
||
.GetAwaiter()
|
||
.GetResult();
|
||
}
|
||
}
|
||
|
||
private void BeginReceive()
|
||
{
|
||
if (this.Config.GetValue(TouchSocketConfigExtension.SslOptionProperty) is ClientSslOption sslOption)
|
||
{
|
||
this.GetTcpCore().Authenticate(sslOption);
|
||
_ = this.GetTcpCore().BeginSslReceive();
|
||
}
|
||
else
|
||
{
|
||
this.GetTcpCore().BeginIocpReceive();
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 异步连接服务器
|
||
/// </summary>
|
||
protected async Task TcpConnectAsync(int timeout, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
await this.m_semaphore.WaitAsync();
|
||
if (this.m_online)
|
||
{
|
||
return;
|
||
}
|
||
if (this.DisposedValue)
|
||
{
|
||
throw new ObjectDisposedException(this.GetType().FullName);
|
||
}
|
||
if (this.Config == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(this.Config), "配置文件不能为空。");
|
||
}
|
||
var iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException(nameof(IPHost), "iPHost不能为空。");
|
||
this.MainSocket.SafeDispose();
|
||
var socket = this.CreateSocket(iPHost);
|
||
await this.PrivateOnConnecting(new ConnectingEventArgs(socket));
|
||
|
||
#if NET6_0_OR_GREATER
|
||
using CancellationTokenSource cancellationTokenSource = new(timeout);
|
||
using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken);
|
||
try
|
||
{
|
||
await socket.ConnectAsync(iPHost.EndPoint, stoppingToken.Token);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
throw new TimeoutException("连接超时");
|
||
}
|
||
await SuccessAsync(socket);
|
||
#else
|
||
|
||
using CancellationTokenSource cancellationTokenSource = new();
|
||
using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken);
|
||
var task = Task.Factory.FromAsync(socket.BeginConnect(iPHost.EndPoint, null, null), socket.EndConnect);
|
||
var result = await Task.WhenAny(task, Task.Delay(timeout, stoppingToken.Token));
|
||
if (result == task)
|
||
{
|
||
cancellationTokenSource.Cancel();
|
||
if (task.Exception != null)
|
||
{
|
||
socket?.SafeDispose();
|
||
throw task.Exception;
|
||
}
|
||
else
|
||
{
|
||
await SuccessAsync(socket);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
socket?.SafeDispose();
|
||
throw new TimeoutException("连接超时");
|
||
}
|
||
|
||
|
||
|
||
|
||
#endif
|
||
async Task SuccessAsync(Socket socket)
|
||
{
|
||
this.m_online = true;
|
||
this.SetSocket(socket);
|
||
this.BeginReceive();
|
||
await (this.PrivateOnConnected(new ConnectedEventArgs()));
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
this.m_semaphore.Release();
|
||
}
|
||
}
|
||
|
||
|
||
/// <inheritdoc/>
|
||
public virtual ITcpClient Connect(int timeout = 5000)
|
||
{
|
||
this.TcpConnect(timeout);
|
||
return this;
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public virtual async Task<ITcpClient> ConnectAsync(int timeout = 5000)
|
||
{
|
||
await this.TcpConnectAsync(timeout);
|
||
return this;
|
||
}
|
||
/// <inheritdoc/>
|
||
public virtual async Task<ITcpClient> ConnectAsync(int timeout, CancellationToken cancellationToken)
|
||
{
|
||
await TcpConnectAsync(timeout, cancellationToken);
|
||
return this;
|
||
}
|
||
|
||
#endregion Connect
|
||
|
||
#region Receiver
|
||
|
||
private Receiver m_receiver;
|
||
|
||
/// <inheritdoc/>
|
||
public IReceiver CreateReceiver()
|
||
{
|
||
return this.m_receiver ??= new Receiver(this);
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public void ClearReceiver()
|
||
{
|
||
this.m_receiver = null;
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <inheritdoc/>
|
||
public override string ToString()
|
||
{
|
||
return this.GetIPPort();
|
||
}
|
||
|
||
private void TcpCoreBreakOut(TcpCore core, bool manual, string msg)
|
||
{
|
||
this.BreakOut(manual, msg);
|
||
}
|
||
/// <summary>
|
||
/// BreakOut。
|
||
/// </summary>
|
||
/// <param name="manual"></param>
|
||
/// <param name="msg"></param>
|
||
protected void BreakOut(bool manual, string msg)
|
||
{
|
||
lock (this.SyncRoot)
|
||
{
|
||
if (this.m_online)
|
||
{
|
||
this.m_online = false;
|
||
this.MainSocket.SafeDispose();
|
||
this.m_delaySender.SafeDispose();
|
||
this.DataHandlingAdapter.SafeDispose();
|
||
Task.Factory.StartNew(this.PrivateOnDisconnected, new DisconnectEventArgs(manual, msg));
|
||
}
|
||
}
|
||
}
|
||
|
||
private TcpCore GetTcpCore()
|
||
{
|
||
this.ThrowIfDisposed();
|
||
return this.m_tcpCore ?? throw new ObjectDisposedException(this.GetType().Name);
|
||
}
|
||
|
||
|
||
/// <inheritdoc/>
|
||
public override int ReceiveBufferSize
|
||
{
|
||
get => this.GetTcpCore().ReceiveBufferSize;
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public override int SendBufferSize
|
||
{
|
||
get => this.GetTcpCore().SendBufferSize;
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public virtual void SetDataHandlingAdapter(SingleStreamDataHandlingAdapter adapter)
|
||
{
|
||
if (!this.CanSetDataHandlingAdapter)
|
||
{
|
||
throw new Exception($"不允许自由调用{nameof(SetDataHandlingAdapter)}进行赋值。");
|
||
}
|
||
|
||
this.SetAdapter(adapter);
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public ITcpClient Setup(TouchSocketConfig config)
|
||
{
|
||
if (config == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(config));
|
||
}
|
||
|
||
this.ThrowIfDisposed();
|
||
|
||
this.BuildConfig(config);
|
||
|
||
this.PluginsManager.Raise(nameof(ILoadingConfigPlugin.OnLoadingConfig), this, new ConfigEventArgs(config));
|
||
this.LoadConfig(this.Config);
|
||
this.PluginsManager.Raise(nameof(ILoadedConfigPlugin.OnLoadedConfig), this, new ConfigEventArgs(config));
|
||
|
||
return this;
|
||
}
|
||
|
||
private void BuildConfig(TouchSocketConfig config)
|
||
{
|
||
this.Config = config;
|
||
|
||
if (!(config.GetValue(TouchSocketCoreConfigExtension.ContainerProperty) is IContainer container))
|
||
{
|
||
container = new Container();
|
||
}
|
||
|
||
if (!container.IsRegistered(typeof(ILog)))
|
||
{
|
||
container.RegisterSingleton<ILog, LoggerGroup>();
|
||
}
|
||
|
||
if (!(config.GetValue(TouchSocketCoreConfigExtension.PluginsManagerProperty) is IPluginsManager pluginsManager))
|
||
{
|
||
pluginsManager = new PluginsManager(container);
|
||
}
|
||
|
||
if (container.IsRegistered(typeof(IPluginsManager)))
|
||
{
|
||
pluginsManager = container.Resolve<IPluginsManager>();
|
||
}
|
||
else
|
||
{
|
||
container.RegisterSingleton<IPluginsManager>(pluginsManager);
|
||
}
|
||
|
||
if (config.GetValue(TouchSocketCoreConfigExtension.ConfigureContainerProperty) is Action<IContainer> actionContainer)
|
||
{
|
||
actionContainer.Invoke(container);
|
||
}
|
||
|
||
if (config.GetValue(TouchSocketCoreConfigExtension.ConfigurePluginsProperty) is Action<IPluginsManager> actionPluginsManager)
|
||
{
|
||
pluginsManager.Enable = true;
|
||
actionPluginsManager.Invoke(pluginsManager);
|
||
}
|
||
this.Container = container;
|
||
this.PluginsManager = pluginsManager;
|
||
}
|
||
|
||
private void PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
|
||
{
|
||
if (this.m_receiver != null)
|
||
{
|
||
if (this.m_receiver.TryInputReceive(byteBlock, requestInfo))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
this.ReceivedData(new ReceivedDataEventArgs(byteBlock, requestInfo)).GetFalseAwaitResult();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当收到适配器处理的数据时。
|
||
/// </summary>
|
||
/// <param name="e"></param>
|
||
/// <returns>如果返回<see langword="true"/>则表示数据已被处理,且不会再向下传递。</returns>
|
||
protected virtual Task ReceivedData(ReceivedDataEventArgs e)
|
||
{
|
||
return this.PluginsManager.RaiseAsync(nameof(ITcpReceivedPlugin.OnTcpReceived), this, e);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 当即将发送时,如果覆盖父类方法,则不会触发插件。
|
||
/// </summary>
|
||
/// <param name="buffer">数据缓存区</param>
|
||
/// <param name="offset">偏移</param>
|
||
/// <param name="length">长度</param>
|
||
/// <returns>返回值表示是否允许发送</returns>
|
||
protected virtual async Task<bool> SendingData(byte[] buffer, int offset, int length)
|
||
{
|
||
if (this.PluginsManager.GetPluginCount(nameof(ITcpSendingPlugin.OnTcpSending)) > 0)
|
||
{
|
||
var args = new SendingEventArgs(buffer, offset, length);
|
||
await this.PluginsManager.RaiseAsync(nameof(ITcpSendingPlugin.OnTcpSending), this, args).ConfigureAwait(false);
|
||
return args.IsPermitOperation;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载配置
|
||
/// </summary>
|
||
/// <param name="config"></param>
|
||
protected virtual void LoadConfig(TouchSocketConfig config)
|
||
{
|
||
this.RemoteIPHost = config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty);
|
||
this.Logger ??= this.Container.Resolve<ILog>();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置适配器,该方法不会检验<see cref="CanSetDataHandlingAdapter"/>的值。
|
||
/// </summary>
|
||
/// <param name="adapter"></param>
|
||
protected void SetAdapter(SingleStreamDataHandlingAdapter adapter)
|
||
{
|
||
this.ThrowIfDisposed();
|
||
if (adapter is null)
|
||
{
|
||
throw new ArgumentNullException(nameof(adapter));
|
||
}
|
||
|
||
if (this.Config != null)
|
||
{
|
||
adapter.Config(this.Config);
|
||
}
|
||
|
||
adapter.Logger = this.Logger;
|
||
adapter.OnLoaded(this);
|
||
adapter.ReceivedCallBack = this.PrivateHandleReceivedData;
|
||
adapter.SendCallBack = this.DefaultSend;
|
||
adapter.SendAsyncCallBack = this.DefaultSendAsync;
|
||
this.DataHandlingAdapter = adapter;
|
||
}
|
||
|
||
private Socket CreateSocket(IPHost iPHost)
|
||
{
|
||
Socket socket;
|
||
if (iPHost.HostNameType == UriHostNameType.Dns)
|
||
{
|
||
socket = new Socket(SocketType.Stream, ProtocolType.Tcp)
|
||
{
|
||
SendTimeout = this.Config.GetValue(TouchSocketConfigExtension.SendTimeoutProperty)
|
||
};
|
||
}
|
||
else
|
||
{
|
||
socket = new Socket(iPHost.EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
|
||
{
|
||
SendTimeout = this.Config.GetValue(TouchSocketConfigExtension.SendTimeoutProperty)
|
||
};
|
||
}
|
||
if (this.Config.GetValue(TouchSocketConfigExtension.KeepAliveValueProperty) is KeepAliveValue keepAliveValue)
|
||
{
|
||
#if NET45_OR_GREATER
|
||
|
||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
|
||
socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValue.KeepAliveTime, null);
|
||
#else
|
||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||
{
|
||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
|
||
socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValue.KeepAliveTime, null);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
var noDelay = this.Config.GetValue(TouchSocketConfigExtension.NoDelayProperty);
|
||
if (noDelay != null)
|
||
{
|
||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, noDelay);
|
||
}
|
||
|
||
if (this.Config.GetValue(TouchSocketConfigExtension.BindIPHostProperty) != null)
|
||
{
|
||
if (this.Config.GetValue(TouchSocketConfigExtension.ReuseAddressProperty))
|
||
{
|
||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||
}
|
||
socket.Bind(this.Config.GetValue(TouchSocketConfigExtension.BindIPHostProperty).EndPoint);
|
||
}
|
||
return socket;
|
||
}
|
||
|
||
#region 发送
|
||
|
||
#region 同步发送
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
/// <param name="requestInfo"></param>
|
||
/// <exception cref="NotConnectedException"></exception>
|
||
/// <exception cref="OverlengthException"></exception>
|
||
/// <exception cref="Exception"></exception>
|
||
public void Send(IRequestInfo requestInfo)
|
||
{
|
||
if (this.DisposedValue)
|
||
{
|
||
return;
|
||
}
|
||
if (this.DataHandlingAdapter == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
|
||
}
|
||
if (!this.DataHandlingAdapter.CanSendRequestInfo)
|
||
{
|
||
throw new NotSupportedException($"当前适配器不支持对象发送。");
|
||
}
|
||
this.DataHandlingAdapter.SendInput(requestInfo);
|
||
}
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
/// <param name="buffer"><inheritdoc/></param>
|
||
/// <param name="offset"><inheritdoc/></param>
|
||
/// <param name="length"><inheritdoc/></param>
|
||
/// <exception cref="NotConnectedException"><inheritdoc/></exception>
|
||
/// <exception cref="OverlengthException"><inheritdoc/></exception>
|
||
/// <exception cref="Exception"><inheritdoc/></exception>
|
||
public virtual void Send(byte[] buffer, int offset, int length)
|
||
{
|
||
if (this.DataHandlingAdapter == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
|
||
}
|
||
this.DataHandlingAdapter.SendInput(buffer, offset, length);
|
||
}
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
/// <param name="transferBytes"><inheritdoc/></param>
|
||
/// <exception cref="NotConnectedException"><inheritdoc/></exception>
|
||
/// <exception cref="OverlengthException"><inheritdoc/></exception>
|
||
/// <exception cref="Exception"><inheritdoc/></exception>
|
||
public virtual void Send(IList<ArraySegment<byte>> transferBytes)
|
||
{
|
||
if (this.DataHandlingAdapter == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
|
||
}
|
||
|
||
if (this.DataHandlingAdapter.CanSplicingSend)
|
||
{
|
||
this.DataHandlingAdapter.SendInput(transferBytes);
|
||
}
|
||
else
|
||
{
|
||
var length = 0;
|
||
foreach (var item in transferBytes)
|
||
{
|
||
length += item.Count;
|
||
}
|
||
using (var byteBlock = new ByteBlock(length))
|
||
{
|
||
foreach (var item in transferBytes)
|
||
{
|
||
byteBlock.Write(item.Array, item.Offset, item.Count);
|
||
}
|
||
this.DataHandlingAdapter.SendInput(byteBlock.Buffer, 0, byteBlock.Len);
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion 同步发送
|
||
|
||
#region 异步发送
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
/// <param name="buffer"></param>
|
||
/// <param name="offset"></param>
|
||
/// <param name="length"></param>
|
||
/// <exception cref="NotConnectedException"></exception>
|
||
/// <exception cref="OverlengthException"></exception>
|
||
/// <exception cref="Exception"></exception>
|
||
public virtual Task SendAsync(byte[] buffer, int offset, int length)
|
||
{
|
||
this.ThrowIfDisposed();
|
||
if (this.DataHandlingAdapter == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
|
||
}
|
||
return this.DataHandlingAdapter.SendInputAsync(buffer, offset, length);
|
||
}
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
/// <param name="requestInfo"></param>
|
||
/// <exception cref="NotConnectedException"></exception>
|
||
/// <exception cref="OverlengthException"></exception>
|
||
/// <exception cref="Exception"></exception>
|
||
public virtual Task SendAsync(IRequestInfo requestInfo)
|
||
{
|
||
this.ThrowIfDisposed();
|
||
if (this.DataHandlingAdapter == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
|
||
}
|
||
if (!this.DataHandlingAdapter.CanSendRequestInfo)
|
||
{
|
||
throw new NotSupportedException($"当前适配器不支持对象发送。");
|
||
}
|
||
return this.DataHandlingAdapter.SendInputAsync(requestInfo);
|
||
}
|
||
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
/// <param name="transferBytes"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="ArgumentNullException"></exception>
|
||
public virtual Task SendAsync(IList<ArraySegment<byte>> transferBytes)
|
||
{
|
||
this.ThrowIfDisposed();
|
||
if (this.DataHandlingAdapter == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
|
||
}
|
||
if (this.DataHandlingAdapter.CanSplicingSend)
|
||
{
|
||
return this.DataHandlingAdapter.SendInputAsync(transferBytes);
|
||
}
|
||
else
|
||
{
|
||
var length = 0;
|
||
foreach (var item in transferBytes)
|
||
{
|
||
length += item.Count;
|
||
}
|
||
using (var byteBlock = new ByteBlock(length))
|
||
{
|
||
foreach (var item in transferBytes)
|
||
{
|
||
byteBlock.Write(item.Array, item.Offset, item.Count);
|
||
}
|
||
return this.DataHandlingAdapter.SendInputAsync(byteBlock.Buffer, 0, byteBlock.Len);
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion 异步发送
|
||
|
||
/// <inheritdoc/>
|
||
public void DefaultSend(byte[] buffer, int offset, int length)
|
||
{
|
||
if (this.SendingData(buffer, offset, length).GetFalseAwaitResult())
|
||
{
|
||
if (this.m_delaySender != null)
|
||
{
|
||
this.m_delaySender.Send(new QueueDataBytes(buffer, offset, length));
|
||
return;
|
||
}
|
||
this.GetTcpCore().Send(buffer, offset, length);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public async Task DefaultSendAsync(byte[] buffer, int offset, int length)
|
||
{
|
||
if (await this.SendingData(buffer, offset, length))
|
||
{
|
||
await this.GetTcpCore().SendAsync(buffer, offset, length);
|
||
}
|
||
}
|
||
|
||
#endregion 发送
|
||
|
||
private void SetSocket(Socket socket)
|
||
{
|
||
if (socket == null)
|
||
{
|
||
this.IP = null;
|
||
this.Port = -1;
|
||
return;
|
||
}
|
||
|
||
this.IP = socket.RemoteEndPoint.GetIP();
|
||
this.Port = socket.RemoteEndPoint.GetPort();
|
||
this.MainSocket = socket;
|
||
var delaySenderOption = this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty);
|
||
if (delaySenderOption != null)
|
||
{
|
||
this.m_delaySender = new DelaySender(delaySenderOption, this.GetTcpCore().Send);
|
||
}
|
||
this.m_tcpCore.Reset(socket);
|
||
this.m_tcpCore.OnReceived = this.HandleReceived;
|
||
this.m_tcpCore.OnBreakOut = this.TcpCoreBreakOut;
|
||
if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue)
|
||
{
|
||
this.m_tcpCore.MinBufferSize = minValue;
|
||
}
|
||
|
||
if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue)
|
||
{
|
||
this.m_tcpCore.MaxBufferSize = maxValue;
|
||
}
|
||
}
|
||
|
||
private void HandleReceived(TcpCore core, ByteBlock byteBlock)
|
||
{
|
||
try
|
||
{
|
||
if (this.DisposedValue)
|
||
{
|
||
return;
|
||
}
|
||
if (this.ReceivingData(byteBlock).GetFalseAwaitResult())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (this.DataHandlingAdapter == null)
|
||
{
|
||
this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription());
|
||
return;
|
||
}
|
||
this.DataHandlingAdapter.ReceivedInput(byteBlock);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
this.Logger.Log(LogLevel.Error, this, "在处理数据时发生错误", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当收到原始数据
|
||
/// </summary>
|
||
/// <param name="byteBlock"></param>
|
||
/// <returns>如果返回<see langword="true"/>则表示数据已被处理,且不会再向下传递。</returns>
|
||
protected virtual Task<bool> ReceivingData(ByteBlock byteBlock)
|
||
{
|
||
if (this.PluginsManager.GetPluginCount(nameof(ITcpReceivingPlugin.OnTcpReceiving)) > 0)
|
||
{
|
||
return this.PluginsManager.RaiseAsync(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock));
|
||
}
|
||
return Task.FromResult(false);
|
||
}
|
||
|
||
|
||
}
|
||
} |